package com.atguigu.tingshu.payment.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.RabbitConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AssertUtil;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.strategy.PayTypeContext;
import com.atguigu.tingshu.payment.strategy.PayTypeStrategy;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

	@Autowired
	private PaymentInfoService paymentInfoService;
    @Autowired
    private WxPayV3Config wxPayV3Config;
    @Autowired
    RSAAutoCertificateConfig rsaAutoCertificateConfig;
    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private PayTypeContext payTypeContext;
    @Override
    public JSONObject createJsapi(String payType, String orderNo) {
        PayTypeStrategy payTypeStrategy = payTypeContext.getPayTypeStrategy(payType);
        return payTypeStrategy.createJsapi(payType,orderNo);
    }



    @Override
    public void wxPayNotify(HttpServletRequest request, Map<String, Object> params) {
        //请求头：验签使用 保证数据不会被篡改
        String serial = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");

        //请求体：用户支付的结果  params

        //1、验签： 可以使用Wechatpay-Timestamp、Wechatpay-Nonce头+ params的json格式字符串
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .timestamp(timestamp)
                .signature(signature)
                .body(JSONObject.toJSONString(params))
                .build();
        //   结合 Wechatpay-Serial Wechatpay-Signature 验签
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        //使用解析器解析参数  解析成功的结果使用Notification对象接收
//        Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        System.out.println("=====================");
        System.out.println(JSONObject.toJSONString(transaction));
        //transaction  transactionId:表示wx支付订单生成的id   successTime：表示成功的时间
        if(transaction==null) {
            throw new GuiguException(ResultCodeEnum.PAYMENT_CALLBACK_ERROR);
        }
        //查询交易日志：
        String tradeNo = transaction.getOutTradeNo();//获取异步回调传入的订单号
        PaymentInfo paymentInfo = paymentInfoService.getOne(Wrappers.lambdaQuery(PaymentInfo.class)
                .eq(PaymentInfo::getOrderNo, tradeNo));
        if(paymentInfo==null || !paymentInfo.getPaymentStatus().equals(SystemConstant.PAYMENT_STATUS_UNPAID)){
            return; //
        }
        PayTypeStrategy payTypeStrategy = payTypeContext.getPayTypeStrategy(paymentInfo.getPaymentType());
        //通过策略对象处理异步回调的业务
        payTypeStrategy.wxNotify(transaction);

    }

    @Override
    public boolean queryPayStatus(String orderNo) {
        //判断订单号是否存在
        PaymentInfo paymentInfo = paymentInfoService.getOne(Wrappers.lambdaQuery(PaymentInfo.class)
                .eq(PaymentInfo::getOrderNo, orderNo));
        if(paymentInfo==null){ //查询的订单号不存在！
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        //查询的订单 状态已经更新过了  不需要访问wx服务端查询
        if(!paymentInfo.getPaymentStatus().equals(SystemConstant.PAYMENT_STATUS_UNPAID)){
            if(paymentInfo.getPaymentStatus().equals(SystemConstant.PAYMENT_STATUS_PAID)){
                return true;
            }
            return false;
        }

        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxPayV3Config.getMerchantId());
        request.setOutTradeNo(orderNo);

        JsapiServiceExtension serviceExtension = new JsapiServiceExtension.Builder()
                .config(rsaAutoCertificateConfig)
                .build();

        com.wechat.pay.java.service.payments.model.Transaction transaction = serviceExtension
                .queryOrderByOutTradeNo(request);
        if(transaction.getTradeState().equals(com.wechat.pay.java.service.payments.model
                .Transaction.TradeStateEnum.SUCCESS)){ //支付成功
            //更新交易日志的状态 和 订单状态
            Map<String,String> map = new HashMap<>();//封装消息内容
            map.put("orderNo",transaction.getOutTradeNo());
            map.put("status" , SystemConstant.PAYMENT_STATUS_PAID);
            rabbitTemplate.convertAndSend(RabbitConstant.PAYMENT_EXCHANGE ,
                    RabbitConstant.PAYMENT_STATE_RK , map);
            Transaction trans = new Transaction();
//            trans.setOutTradeNo(orderNo);
//            trans.setSuccessTime(transaction.getSuccessTime());
            BeanUtils.copyProperties(transaction , trans);
            paymentInfoService.updatePaymentInfoStatus(trans,SystemConstant.PAYMENT_STATUS_PAID);

//        else if(transaction.getTradeState().equals(com.wechat.pay.java.service.payments.model
//                .Transaction.TradeStateEnum.CLOSED)){// 支付关闭
//
//        }else if(transaction.getTradeState().equals(com.wechat.pay.java.service.payments.model
//                .Transaction.TradeStateEnum.REFUND)){ // 已退款
            return true;
        }else{ //支付失败：未支付、支付错误....
            //更新交易日志的状态 和 订单状态
            Map<String,String> map = new HashMap<>();//封装消息内容
            map.put("orderNo",transaction.getOutTradeNo());
            map.put("status" , SystemConstant.PAYMENT_STATUS_CLOSE);
            rabbitTemplate.convertAndSend(RabbitConstant.PAYMENT_EXCHANGE ,
                    RabbitConstant.PAYMENT_STATE_RK , map);
            Transaction trans = new Transaction();
//            trans.setOutTradeNo(orderNo);
//            trans.setSuccessTime(transaction.getSuccessTime());
            BeanUtils.copyProperties(transaction , trans);
            paymentInfoService.updatePaymentInfoStatus(trans,SystemConstant.PAYMENT_STATUS_CLOSE);
            return false;
        }
    }
//    @GlobalTransactional//开启分布式事务
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public void refund(String orderNo) {
        //1、如果订单状态不是支付成功 不能退款
        PaymentInfo paymentInfo = paymentInfoService.getOne(Wrappers.lambdaQuery(PaymentInfo.class)
                .eq(PaymentInfo::getOrderNo, orderNo)
                .eq(PaymentInfo::getPaymentStatus , SystemConstant.PAYMENT_STATUS_PAID));
        if(paymentInfo==null){//订单不存在或者未支付
            throw new GuiguException(ResultCodeEnum.ORDER_REFUND_ERROR);
        }
        //2、先判断订单是否支持退款
        Result<OrderInfo> orderInfoResult = orderInfoFeignClient.findOrderInfoByOrderNo(orderNo);
        AssertUtil.resultAssert(orderInfoResult,ResultCodeEnum.ORDER_REFUND_ERROR);
        OrderInfo orderInfo = orderInfoResult.getData();

        if(orderInfo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_VIP)){
            //只有 已生效的vip不能退款！
            Result<Boolean> refundResult = userInfoFeignClient.isSupportRefund(orderNo);
            AssertUtil.resultAssert(refundResult,ResultCodeEnum.ORDER_REFUND_ERROR);
            if(!refundResult.getData()){
                throw new GuiguException(ResultCodeEnum.ORDER_REFUND_NOT_SUPPORT);
            }
        }

        //3、访问微信接口实现退款

        RefundService service = new RefundService.Builder()
                .config(rsaAutoCertificateConfig)
                .build();
        //封装退款参数
        CreateRequest request  =new CreateRequest();
        request.setOutTradeNo(orderNo);//商户的要退款的订单号
        //可以生成全局唯一的退款单号
        String refundNo = IdUtil.getSnowflakeNextIdStr();
        request.setOutRefundNo(refundNo);
        request.setReason("不想要了");
        //退款信息对象
        AmountReq amount = new AmountReq();
        //paymentInfo.getAmount()
        amount.setTotal(1L);//订单总金额
        amount.setRefund(1L);//需要退款的金额
        amount.setCurrency("CNY");//币种
        request.setAmount(amount);
//        request.setNotifyUrl(""); // 内网穿透地址+ 处理退款异步回调的接口！！    退款成功的业务 在异步回调接口中编写
        //发起请求申请退款 得到响应
        Refund refund = service.create(request);
        //  微信服务端响应是异步响应，可能还未退款成功就响应了
        // 可以配置异步回调接口： notify_url
        if(refund.getStatus().equals(Status.SUCCESS)){
            //退款成功
            paymentInfoService.update(Wrappers.lambdaUpdate(PaymentInfo.class)
                    .eq(PaymentInfo::getOrderNo,orderNo)
                    .eq(PaymentInfo::getPaymentStatus , SystemConstant.PAYMENT_STATUS_PAID)
                    .set(PaymentInfo::getPaymentStatus , SystemConstant.PAYMENT_STATUS_REFUND));


        }else if(refund.getStatus().equals(Status.PROCESSING)){
           //退款处理中
            //主动查询退款状态
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            QueryByOutRefundNoRequest queryByOutRefundNoRequest = new QueryByOutRefundNoRequest();
            queryByOutRefundNoRequest.setOutRefundNo(refundNo);
            refund = service.queryByOutRefundNo(queryByOutRefundNoRequest);
            while(refund.getStatus()
                    .equals(Status.PROCESSING)){
                try {
                    Thread.sleep(100);
                    refund = service.queryByOutRefundNo(queryByOutRefundNoRequest);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //退款成功：
            //执行退款成功的业务！！！！
            if(refund.getStatus().equals(Status.SUCCESS)){
                //.....
                //1、更新订单状态：为退款状态
                // TODO  发送mq消息 修改订单状态
                Map<String,String> map = new HashMap<>();//封装消息内容
                map.put("orderNo",orderNo);
                map.put("status" ,SystemConstant.PAYMENT_STATUS_REFUND); //1407 退款
                map.put("itemType" ,orderInfo.getItemType() );
                rabbitTemplate.convertAndSend(RabbitConstant.PAYMENT_EXCHANGE ,
                        RabbitConstant.PAYMENT_STATE_RK , map);
                //2、删除用户购买记录

                paymentInfoService.update(Wrappers.lambdaUpdate(PaymentInfo.class)
                        .eq(PaymentInfo::getOrderNo,orderNo)
                        .eq(PaymentInfo::getPaymentStatus , SystemConstant.PAYMENT_STATUS_PAID)
                        .set(PaymentInfo::getPaymentStatus , SystemConstant.PAYMENT_STATUS_REFUND));
            }


        }else{
            //退款失败
        }
        //因为没有退款日志表： 将退款日志存到 交易日志表中！(orderNo 使用 退款编号设置)
        PaymentInfo refundInfo = new PaymentInfo();
        refundInfo.setUserId(AuthContextHolder.getUserId());
//        paymentInfo.setPaymentType(payType);
        refundInfo.setOrderNo(refundNo);//退款号
        refundInfo.setPayWay(orderInfo.getPayWay());
        refundInfo.setAmount(new BigDecimal(refund.getAmount().getRefund()));
        refundInfo.setCallbackTime(new DateTime(refund.getSuccessTime()).toDate());
        refundInfo.setContent("退款记录");
        refundInfo.setCallbackContent(JSONObject.toJSONString(refund));
        //使用1401状态
        refundInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_REFUND);
        paymentInfoService.save(refundInfo);
        //4、修改订单的状态 和交易日志的状态为已退款！
        // 如果退款订单是 vip订单： 如果vip未生效 可以 删除vip购买记录 ，如果vip已生效不支持退款！(user_info  user_vip_service)
        // 如果退款订单是 专辑订单： 删除专辑订单购买记录 和 声音购买列表
        // 如果退款订单是 声音订单： 删除声音的购买记录



    }

}
